<!-- OpenCV.js官方文档 https://docs.opencv.org/4.5.5/d0/d84/tutorial_js_usage.html -->
<!-- OpenCV.js官方文档 Image InRange Example https://docs.opencv.org/3.4.8/db/d64/tutorial_js_colorspaces.html -->

<!-- OpenCV.js 官方文档 cvtColor inRange https://docs.opencv.org/4.5.5/db/d64/tutorial_js_colorspaces.html -->
<!-- OpenCV.js 官方文档 Image Threshold https://docs.opencv.org/4.5.5/d7/dd0/tutorial_js_thresholding.html -->
<!-- OpenCV.js官方文档 contours 轮廓检测 https://docs.opencv.org/4.5.5/d0/d43/tutorial_js_table_of_contents_contours.html -->
<!-- OpenCV.js官方文档 contours 轮廓检测-(外接矩形) https://docs.opencv.org/4.5.5/dc/dcf/tutorial_js_contour_features.html -->
<!-- OpenCV.js官方文档 contours 轮廓检测-形状匹配 https://docs.opencv.org/4.5.5/d8/d1c/tutorial_js_contours_more_functions.html -->


<!-- 获取图片中指定颜色部分 https://blog.csdn.net/qq_40456669/article/details/93375709 -->
<!-- opencv数字识别（博文） https://blog.csdn.net/Rosen_er/article/details/104234232 -->
<!-- 信用卡数字识别 https://blog.csdn.net/weixin_43227526/article/details/104653957 -->

<!-- 图像基本运算及掩膜 https://blog.csdn.net/u011028345/article/details/77278467 -->

<!-- 
    遇到的问题
        1. cv.inRange使用（更原生有区别，要看官网示例）
            Image InRange Example https://docs.opencv.org/3.4.8/db/d64/tutorial_js_colorspaces.html
    !!!!2. cv.inRange拿到的数据格式是 cv.CV_8U，作为模板图片使用有问题
            mask_.convertTo(mask_, cv.CV_8UC4);// 没转化成功

            用imshow，imread来转化
            cv.imshow("resultCanvas", mask_);
            mask_ = cv.imread("resultCanvas");
 -->

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>opencv使用——数字识别——二值化,获取指定范围颜色,模板匹配，轮廓匹配</title>
    <!-- <script src='https://docs.opencv.org/版本号/opencv.js'></script> -->
    <!-- OpenCV.js 4.5.5 版本 -->
    <!-- <script src='https://docs.opencv.org/4.5.5/opencv.js'></script> -->

    <!-- <script src='./opencv_4.5.5.js' type="module"></script> -->

    <!-- You may want to load opencv.js asynchronously by async attribute in <script> tag. To be notified when opencv.js is ready, you can register a callback to onload attribute. -->
    <script async src="./opencv_4.5.5.js" onload="openCvReady()" type="text/javascript"></script>



</head>

<body>
    <!-- ./bag_rune.jpg -->
    <!-- <canvas id="imgSearchCanvas" width="120" height="120"></canvas>
    <canvas id="resultCanvas" width="120" height="120"></canvas> -->

    <!-- ./screenshot1.jpg -->
    <!-- <canvas style="display: none;" id="imgSearchCanvas" width="1080" height="2400"></canvas>
    
    -->
    <canvas style="display: none;" id="imgSearchCanvas" width="120" height="120"></canvas>

    <!-- <canvas id="resultCanvas" width="1080" height="2400"></canvas> -->

    <canvas id="t1" width="120" height="120"></canvas>
    <canvas id="t2" width="120" height="120"></canvas>
    <canvas id="t3" width="120" height="120"></canvas>
    <canvas id="t4" width="120" height="120"></canvas>
    <canvas id="t5" width="120" height="120"></canvas>
    <canvas id="resultCanvas" width="120" height="120"></canvas>

    <hr />
    <!-- <canvas id="templateCanvasInput" width="70" height="70"></canvas> -->
    <canvas id="templateCanvasInput" width="120" height="120"></canvas>
    <div id="templ_list"></div>

    <!-- ./nums/num_1.jpg -->
    <!-- <canvas id="templateCanvasInput" width="40" height="40"></canvas> -->

    <!-- ./runes/rune_19_COL.jpg -->
    <!-- <canvas id="templateCanvasInput" width="80" height="80"></canvas> -->

    <!-- ./bag_border.png -->
    <!-- <canvas id="templateCanvasInput" width="962" height="795"></canvas> -->

    <!-- <script type="module">
        import * as cv from './opencv_4.5.5.js';
        console.log(cv);
    </script> -->
    <script>
        const NUMS_DATA = [{
            src: "./nums/num_0.png",
            name: "0",
            order: 0
        }, {
            src: "./nums/num_1.png",
            name: "1",
            order: 1
        }, {
            src: "./nums/num_2.png",
            name: "2",
            order: 2
        }, {
            src: "./nums/num_3.png",
            name: "3",
            order: 3
        }, {
            src: "./nums/num_4.png",
            name: "4",
            order: 4
        }, {
            src: "./nums/num_5.png",
            name: "5",
            order: 5
        }, {
            src: "./nums/num_6.png",
            name: "6",
            order: 6
        }, {
            src: "./nums/num_7.png",
            name: "7",
            order: 7
        }, {
            src: "./nums/num_8.png",
            name: "8",
            order: 8
        }, {
            src: "./nums/num_9.png",
            name: "9",
            order: 9
        }];
        let num_imgs = new Map(); // 存放加载的数字图片
        async function openCvReady() {
            // 脚本加载完成
            // console.log(cv);
            // opencv.js 在真正初始化之前加载并触发 onload 事件。为了等到 opencv.js 真正准备好，opencv.js 提供了一个挂机“onRuntimeInitialized”。像这样使用它：

            // var img1 = new Image();
            // img.crossOrigin = 'anonymous';
            // img1.src = './img1.png';
            console.log(1);

            // var src1 = "./img1.png";
            // var src2 = "./img2.png";

            // var src1 = "./icons.png";
            var src1 = "./bag_rune.jpg";
            // var src2 = "./icon.png";
            // var src2 = "./icon_l.png";

            // var src1 = "./screenshot1.jpg";

            var src2 = "./nums/num_1.jpg";
            // var src2 = "./runes/rune_19_COL.jpg";
            // var src2 = "./bag_border.png";

            await Promise.all([await loadImg(src1).then(img1 => {
                // console.log(img1);
                var canvas1 = document.getElementById('imgSearchCanvas');
                var ctx1 = canvas1.getContext('2d');
                // img1.onload = function () {
                //     ctx1.drawImage(img1, 0, 0);
                // }
                ctx1.drawImage(img1, 0, 0);


                var canvas3 = document.getElementById('resultCanvas');
                var ctx3 = canvas3.getContext('2d');
                ctx3.drawImage(img1, 0, 0);

                console.log(12);
            }), await loadImg(src2).then(img2 => {
                // console.log(img2);
                var canvas2 = document.getElementById('templateCanvasInput');
                var ctx2 = canvas2.getContext('2d', {
                    willReadFrequently: true,
                });
                ctx2.drawImage(img2, 0, 0);
                console.log(13);
            }), ...NUMS_DATA.map(async (el) => {
                let src = el.src;
                let name = el.name;
                return (
                    await loadImg(src).then(img => {
                        num_imgs.set(name, img);
                    })
                );
            })]);

            console.log(2);

            /** @todo 为啥批量加载图片时 cv.onRuntimeInitialized不自动执行了 */
            cv['onRuntimeInitialized'] = () => {
                console.log(3);
                // do all your work here



                /**
                 * 
                 * 二值化图片
                let img = cv.imread("imgSearchCanvas");
                let clone_img = img.clone(); //原图备份

                // cv.imshow('resultCanvas', clone_img);

                cv.cvtColor(img, img, cv.COLOR_BGR2GRAY); //图片转灰度
                // cv.imshow('resultCanvas', img);
                cv.threshold(img, img, 100, 255, cv.THRESH_BINARY); //二值化
                // cv.threshold(img, img, 100, 255, cv.THRESH_BINARY_INV); //二值化
                let clone_img1 = img.clone(); //备份二值化后的原图

                cv.imshow('resultCanvas', clone_img1);
                */

                /**
                //  Image InRange Example https://docs.opencv.org/3.4.8/db/d64/tutorial_js_colorspaces.html
                let src = cv.imread('canvasInput');
                let dst = new cv.Mat();
                let low = new cv.Mat(src.rows, src.cols, src.type(), [0, 0, 0, 0]);
                let high = new cv.Mat(src.rows, src.cols, src.type(), [150, 150, 150, 255]);
                // You can try more different parameters
                cv.inRange(src, low, high, dst);
                cv.imshow('canvasOutput', dst);
                src.delete();
                dst.delete();
                low.delete();
                high.delete();
                */




                // 获取图片指定颜色部分 
                let src = cv.imread("imgSearchCanvas");


                // CV_8uc1 单颜色通道 8位,CV_8uc2 2颜色通道 16位, CV_8uc3 3颜色通道 24位,CV_8uc4 4颜色通道 32位


                // console.log("src.type:",src.type()); // 24 cv: cv.CV_8UC4
                let clone_src = src.clone(); //原图备份
                // cv.namedWindow("input", cv.WINDOW_AUTOSIZE);
                // cv.imshow("resultCanvas", src);
                // let hsv = cv.cvtColor(src, src, cv.COLOR_BGR2HSV);

                // num && level
                let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [240, 0, 0, 255]);
                let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [255, 255, 255, 255]);

                // rune
                // let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [141, 174, 183, 255]);
                // let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [203, 212, 212, 255]);

                // rune 可以被模板匹配使用
                // let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [140, 140, 140, 255]);
                // let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [200, 200, 200, 255]);

                // 背包范围提取
                // let low_hsv = new cv.Mat(src.rows, src.cols, src.type(), [40, 40, 40, 255]);
                // let high_hsv = new cv.Mat(src.rows, src.cols, src.type(), [70, 70, 70, 255]);

                let dst_b_w = new cv.Mat();
                /**
                 * We use the function: cv.inRange (src, lowerb, upperb, dst)

                    Parameters
                    src	    first input image.
                    lowerb	inclusive lower boundary Mat of the same size as src.
                    upperb	inclusive upper boundary Mat of the same size as src.
                    dst	    output image of the same size as src and cv.CV_8U type.


                */
                cv.inRange(src, low_hsv, high_hsv, dst_b_w);

                cv.imshow("resultCanvas", dst_b_w);
                dst_b_w = cv.imread("resultCanvas");

                /** 识别数字只选择图片右下角*/
                let numRect = new cv.Rect(60, 60, 60, 60);
                let num_b_w = dst_b_w.roi(numRect).clone();
                // let num_b_w = dst_b_w.clone();

                // console.log("output.type:",dst_b_w.type());// 0 cv.CV_8U
                // cv.imshow("resultCanvas", src);



                // 模板匹配
                // let src = cv.imread('imgSearchCanvas');
                // num_b_w.convertTo(num_b_w, cv.CV_8UC4); // 没用


                /**
                // -------轮廓匹配
                src = num_b_w;
                let templ = cv.imread('templateCanvasInput');

                let dst = new cv.Mat();
                let mask = new cv.Mat();


                try {
                    cv.matchTemplate(src, templ, dst, cv.TM_CCOEFF_NORMED, mask);
                } catch (error) {
                    console.error(error);
                }

                let result = cv.minMaxLoc(dst, mask);
                let maxPoint = result.maxLoc;
                let color = new cv.Scalar(255, 0, 0, 255);
                let point = new cv.Point(maxPoint.x + templ.cols, maxPoint.y + templ.rows);
                cv.rectangle(src, maxPoint, point, color, 2, cv.LINE_8, 0);
                cv.imshow('resultCanvas', src);

                src.delete();
                dst.delete();
                mask.delete();
                */



                /** -------轮廓匹配
                 *  t1 显示num二值化图片
                 * 
                 */

                cv.imshow('t1', num_b_w);
                /**
                 * @link 官方 https://docs.opencv.org/4.5.5/d0/d43/tutorial_js_table_of_contents_contours.html
                 * contours, hierarchy = cv2.findContours(img,mode,method)
                 * 
                 * 参数:
                 * mode 轮廓检索模式 
                 *      RETR_EXTERNAL:只检索最外面的轮廓
                 *      RETR_LIST:检索所有的轮廓，并将其保存到一条链表当中
                 *      RETR_CCOMP:检索所有的轮廓，并将他们组织为两层：顶层是各部分的外部边界，第二层是空洞的边界；
                 *      RETR_TREE: 检索所有的轮廓，并重构嵌套轮廓的整个层次
                 * 
                 * method:轮廓逼近方法
                 *      CHAIN_APPROX_NONE: 以Freeman链码的方式输出轮廓，所有的其他方法输出多边形（顶点的序列）
                 *      CHAIN_APPROX_SIMPLE: 压缩水平的、垂直的和斜的部分，也就是，函数只保留他们的终点部分
                 * 
                 * 返回值:
                 *      contours 轮廓
                 *      hierarchy 层级
                 */
                // let res = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE);
                src = num_b_w;
                // console.log("src.type:",src.type()); // 24 cv: cv.CV_8UC4
                let contours = new cv.MatVector();
                let hierarchy = new cv.Mat();
                let dst = cv.Mat.zeros(src.cols, src.rows, cv.CV_8UC3);
                // console.log("dst.type:",dst.type()); // 16 cv: cv.CV_8UC3


                // 将三通道图像（彩色图）转化为单通道图（灰度图）；第三个参数flag 转换模式 cv2.COLOR_RGB2GRAY 彩色转灰度 cv2.COLOR_GRAY2RGB 单通道转多通道
                cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
                // console.log("src.type:",src.type()); // 0 cv: cv.CV_8U
                cv.imshow('t2', src);


                cv.threshold(src, src, 220, 255, cv.THRESH_BINARY);
                // console.log("src.type:",src.type()); // 0 cv: cv.CV_8U
                cv.imshow('t3', src);
                // console.log("src.type:",src.type()); // 0 cv: cv.CV_8U


                // let src_8U = new cv.Mat();
                // let src_8U = src.clone();
                // 从大范围到小范围要先转再转类型
                // let out = src_8U / 257; // NaN
                // let out = cv.divide(src_8U, 257); // 不是这样用的
                // let out = src_8U;
                // console.log("out:", out);
                // src.convertTo(out, cv.CV_8U);

                /** 
                 * convertTo(m, rtype, alpha = 1, beta = 0)
                 *  m	output matrix; if it does not have a proper size or type before the operation, it is reallocated.
                 *  rtype	desired output matrix type or, rather, the depth since the number of channels are the same as the input has; if rtype is negative, the output matrix will have the same type as the input.
                 *  alpha	optional scale factor.
beta	optional delta added to the scaled values.
                 */
                // out.convertTo(out, cv.CV_8U, 255.0);
                // let out = new cv.Mat(src.rows, src.cols, cv.CV_8U);
                // src.convertTo(out, cv.CV_8U); // 无效，转化的通道数是一致的，无法进行通道数转换
                // out = cv.imread("t1", cv.CV_8U);// 无效 imread只有一个参数
                // console.log("out.type:", out.type());

                cv.findContours(src, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

                let color1 = new cv.Scalar(255, 0, 255); // 轮廓颜色
                let color2 = new cv.Scalar(255, 255, 0); // 外接矩形颜色
                /**
                for (let i = 0; i < contours.size(); ++i) {
                    // let color = new cv.Scalar(Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255));

                    cv.drawContours(dst, contours, i, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制

                    // 外接矩形绘制
                    let cnt = contours.get(i);
                    let rect = cv.boundingRect(cnt);
                    let point1 = new cv.Point(rect.x, rect.y);
                    let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);
                    cv.rectangle(dst, point1, point2, color2, 1, cv.LINE_AA, 0);
                    cnt.delete();
                }
                */
                let contours_order = 1;
                cv.drawContours(dst, contours, contours_order, color1, 1, cv.LINE_8, hierarchy, 100); // 轮廓绘制
                // 外接矩形绘制
                let cnt = contours.get(contours_order);
                let rect = cv.boundingRect(cnt);
                let point1 = new cv.Point(rect.x, rect.y);
                let point2 = new cv.Point(rect.x + rect.width, rect.y + rect.height);
                let dst_c = dst.clone();
                cv.rectangle(dst, point1, point2, color2, 1, cv.LINE_AA, 0);
                cnt.delete();


                cv.imshow('t4', dst);

                dst = dst_c.roi(rect);

                cv.imshow('t5', dst);


                // cv.imshow('resultCanvas', src);


                // ----------轮廓匹配
                // 导入模板
                let ele_templ_list = document.getElementById("templ_list");
                let content = "";
                for (let i = 0; i < NUMS_DATA.length; i++) {

                    if (i == 0 || i == 8) {
                        continue; // 0和8暂时不考虑
                    }
                    let num = NUMS_DATA[i];
                    let img_num = num_imgs.get(num.name);
                    if (img_num === undefined) {
                        console.warn(`num ${num.name} 图片加载失败`);
                        continue;
                    }
                    // console.log(img_num.width, img_num.height);
                    content +=
                        `<canvas id="${'num_'+num.name}" width="${img_num.width}" height="${img_num.height}"></canvas>`;
                }
                ele_templ_list.innerHTML = content;

                for (let i = 0; i < NUMS_DATA.length; i++) {
                    if (i == 0 || i == 8) {
                        continue; // 0和8暂时不考虑
                    }
                    let num = NUMS_DATA[i];
                    let img_num = num_imgs.get(num.name);
                    if (img_num === undefined) {
                        console.warn(`num ${num.name} 图片加载失败`);
                        continue;
                    }
                    let canvas_id = 'num_' + num.name;
                    let canvas = document.getElementById(canvas_id);
                    let ctx = canvas.getContext('2d', {
                        willReadFrequently: true,
                    });
                    ctx.drawImage(img_num, 0, 0);
                    let templ = cv.imread(canvas_id); // num_x模板

                    cv.cvtColor(templ, templ, cv.COLOR_RGBA2GRAY, 0); //转化为单通道灰度图
                    cv.threshold(templ, templ, 220, 255, cv.THRESH_BINARY); // 3 thresh 阈值，4：maxval最大值


                    // 模板轮廓绘制
                    let contours = new cv.MatVector();
                    let hierarchy = new cv.Mat();
                    let dst = cv.Mat.zeros(templ.cols, templ.rows, cv.CV_8UC3);
                    cv.findContours(templ, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);

                    let contours_order = 0;
                    /**
                     * @todo 数字0和8轮廓要特殊处理，这里不考虑了（游戏中背包符石数量很难达到8（更别说10了），而且没有0这种情况），如果真想处理可以使用cv.minMaxLoc ，遍历所有轮廓，取出四个rect的顶点。
                     */
                    cv.drawContours(dst, contours, contours_order, color1, 1, cv.LINE_8, hierarchy,
                        100); // 轮廓绘制
                    // 外接矩形绘制
                    let cnt = contours.get(contours_order);
                    let rect_t = cv.boundingRect(cnt);
                    let point1 = new cv.Point(rect_t.x, rect_t.y);
                    let point2 = new cv.Point(rect_t.x + rect_t.width, rect_t.y + rect_t.height);
                    // cv.rectangle(dst, point1, point2, color2, 1, cv.LINE_AA, 0);
                    cnt.delete();
                    // cv.imshow(canvas_id, templ);

                    /**
                     * Scaling
                     * Scaling is just resizing of the image. OpenCV comes with a function cv.resize() for this purpose. The size of the image can be specified manually, or you can specify the scaling factor. Different interpolation methods are used. Preferable interpolation methods are cv.INTER_AREA for shrinking and cv.INTER_CUBIC (slow) & cv.INTER_LINEAR for zooming.

                     * We use the function: cv.resize (src, dst, dsize, fx = 0, fy = 0, interpolation = cv.INTER_LINEAR)

                     * Parameters
                     * src	input image
                     * dst	output image; it has the size dsize (when it is non-zero) or the size computed from src.size(), fx, and fy; the type of dst is the same as of src.
                     * dsize	output image size; if it equals zero, it is computed as:
                     * dsize=Size(round(fx∗src.cols),round(fy∗src.rows))
                     * Either dsize or both fx and fy must be non-zero.
                     * fx	scale factor along the horizontal axis; when it equals 0, it is computed as
                    (double)dsize.width/src.cols
                     * fy	scale factor along the vertical axis; when it equals 0, it is computed as
                    (double)dsize.height/src.rows
                     * interpolation	interpolation method(see cv.InterpolationFlags)
                    */
                    // console.log(rect);
                    dst = dst.roi(rect_t);
                    // cv.resize(dst, dst, new cv.Size(rect_t.width, rect_t.height)); // 缩放模板图片

                    cv.imshow(canvas_id, dst);
                }

                /**
                 * @todo 模板匹配+mmCompare是一种方法，那有其他方法吗？总感觉不正规
                 * @todo 试试 cv.matchShapes https://docs.opencv.org/4.5.5/d8/d1c/tutorial_js_contours_more_functions.html
                */
                let similarity = 0;
                let result_num = -1;
                dst = cv.imread("t5");
                for (let i = 0; i < NUMS_DATA.length; i++) {
                    if (i == 0 || i == 8) {
                        continue; // 0和8暂时不考虑
                    }
                    let num = NUMS_DATA[i];
                    let img_num = num_imgs.get(num.name);
                    if (img_num === undefined) {
                        console.warn(`num ${num.name} 图片加载失败`);
                        continue;
                    }
                    let canvas_id = 'num_' + num.name;
                    let templ = cv.imread(canvas_id);
                    // dst
                    let res = mmCompare(dst, templ);
                    // let res = mmCompare(templ, dst);
                    if (res > similarity) {
                        similarity = res;
                        result_num = i;
                    }

                }
                console.log(`数字${result_num}的相似度(${similarity})最高`);


                src.delete();
                dst.delete();
                contours.delete();
                hierarchy.delete();
                // cnt.delete();

            };




        }

        function _Rect(pt1, pt2) {
            let x = pt1.x < pt2.x ? pt1.x : pt2.x;
            let y = pt1.y < pt2.y ? pt1.y : pt2.y;
            let w = pt1.x > pt2.x ? pt1.x : pt2.x - x;
            let h = pt1.y > pt2.y ? pt1.y : pt2.y - y;
            return new cv.Rect(x, y, w, h);
        }

        function loadImg(url) {
            return new Promise((resolve, reject) => {
                let img = new Image();
                // img.crossOrigin = 'anonymous';
                // 跨域 直接用live server运行
                img.src = url;
                img.onload = () => {
                    resolve(img);
                };
                img.onerror = reject;
            });
        }

        /***
         * @function
         * @description 两个图像相似度比较。
         * @description 最后就是模板匹配了，匹配的方式有很多种，这里采用的是对数字和模板进行像素点的匹配，根据相同像素点的百分占比来得出结论，相同像素点越多，说明匹配相似度越高，最后以匹配程度最高的作为最终识别结果
         * @param { cv.Mat } img1 注意保证两个图的通道数一致
         * @param { cv.Mat } img2 注意保证两个图的通道数一致
         * 
         * @returns { float } [0,1]
         * @link https://blog.csdn.net/m0_71741835/article/details/127937354
         * 
         */
        function mmCompare(img1, img2) {
            if (img1.channels() !== img2.channels()) {
                console.error("img1与img2通道数不一致");
                return 0;
            }
            // console.log(img1.channels(), img2.channels());
            let my_temp = new cv.Mat();
            if (img1.cols > img2.cols) {
                // console.log("xxx");
                cv.resize(img2, my_temp, img1.size());
            } else {
                // console.log("yyy");
                cv.resize(img1, my_temp, img2.size());
            }
            let rows, cols;
            let img_point, temp_point; //像素类型uchar

            // console.log(my_temp.rows, img1.rows, img2.rows);
            // console.log(my_temp.cols, img1.cols, img2.cols);
            rows = my_temp.rows;
            cols = my_temp.cols * img2.channels();

            let result, same = 0.0,
                different = 0.0;
            for (let i = 0; i < rows; i++) //遍历图像像素
            {
                //获取像素值
                if (img1.cols > img2.cols) {
                    img_point = img1.ucharPtr(i);
                } else {
                    img_point = img2.ucharPtr(i);
                }
                temp_point = my_temp.ucharPtr(i);
                for (let j = 0; j < cols; j++) {
                    // let test1 = img_point[j];
                    // let test2 = temp_point[j];
                    // console.log(test1, test2);
                    if (img_point[j] == temp_point[j]) {
                        same++; //记录像素相同的个数
                        // if (img_point[j] !== 0) {
                        //     same++;//记录像素相同的个数
                        // }
                    } else {
                        different++; //记录像素不同的个数
                    }

                }
            }
            result = same / (same + different);
            my_temp.delete();
            return result; //返回匹配结果
        }
    </script>
</body>

</html>